/************************************************************************

								Heroes IV
					  Copyright 2000, The 3DO Company

	------------------------------------------------------------------
  						      spellbook_window.cpp

	$Header: /game/spellbook_window.cpp $

	$NoKeywords: $

 ************************************************************************/

// kill the 255+ character identifier warning.  Templates tend to exceed that easily.
#include "precompiled.h"
#pragma warning( disable: 4786 )

#include "spellbook_window.h"

#include "abstract_creature.h"
#include "adaptor_handler.h"
#include "artifact.h"
#include "artifact_effect_type.h"
#include "artifact_icon.h"
#include "artifact_prop_spell_charges.h"
#include "artifact_slot.h"
#include "bitmap_group_cache.h"
#include "blended_bitmap_window.h"
#include "bound_handler.h"
#include "button.h"
#include "button_cache.h"
#include "cached_grail_data_source.h"
#include "elements_of.h"
#include "enum_operations.h"
#include "external_string.h"
#include "format_string.h"
#include "game_replace_keywords.h"
#include "interface_help.h"
#include "item_help_block.h"
#include "spell.h"
#include "spell_icon.h"
#include "spell_icon_window.h"
#include "spell_properties.h"
#include "standard_fonts.h"
#include "string_insensitive_compare.h"
#include "text_window.h"
#include "toggle_button.h"
#include "town_properties.h"
#include "town_type.h"

extern t_bitmap_group_cache const k_frame_bitmaps;
static t_button_cache k_cast_spell_button( "button.spellbook.cast_spell" );
extern t_bitmap_group_cache const    k_arrow_button_bitmaps;



// ------------------------------------------------------------------
// function objects to test spells
// ------------------------------------------------------------------
namespace
{
	// hotkey resource strings
	t_external_string const k_hot_key_damage( "damage.hot_keys" );			// D
	t_external_string const k_hot_key_summoning( "summoning.hot_keys" );	// S
	t_external_string const k_hot_key_curses( "curses.hot_keys" );			// U
	t_external_string const k_hot_key_blessings( "blessings.hot_keys" );	// B
	t_external_string const k_hot_key_all_spells( "all_spells.hot_keys" );	// L
	t_external_string const k_hot_key_adventure( "adventure.hot_keys" );	// A
	t_external_string const k_hot_key_combat( "combat.hot_keys" );			// C
	t_external_string const k_hot_key_index( "index.hot_keys" );			// I
	t_external_string const k_hot_key_artifacts( "artifacts.hot_keys" );	// T

	class t_spell_func : public t_spell_tester
	{
	public:
		typedef bool t_func( t_spell );

		t_spell_func( t_func* function );

		virtual bool operator()( t_spell spell ) const;
	protected:
		t_func* m_function;
	};

	class t_school_tester : public t_spell_tester
	{
	public:
		t_school_tester( t_town_type alignment );

		virtual bool operator()( t_spell spell ) const;
	protected:
		t_town_type m_alignment;
	};
};

// ------------------------------------------------------------------
// test if spell is valid - always returns true
// ------------------------------------------------------------------
bool t_spell_tester::operator ()( t_spell spell ) const
{
	return true;
}

// ------------------------------------------------------------------
// test if spell matches a function
// ------------------------------------------------------------------
t_spell_func::t_spell_func( t_func* function ) : m_function( function )
{
}

bool t_spell_func::operator ()( t_spell spell ) const
{
	return m_function( spell );
}

// ------------------------------------------------------------------
// test is of a a school
// ------------------------------------------------------------------
t_school_tester::t_school_tester( t_town_type alignment )
{
	m_alignment = alignment;
}

// ------------------------------------------------------------------
// test is of a a school
// ------------------------------------------------------------------
bool t_school_tester::operator ()( t_spell spell ) const
{
	return get_spell_alignment( spell ) == m_alignment;
}

// ------------------------------------------------------------------
// window to show detail of a particular spell
// ------------------------------------------------------------------
class t_spellbook_detail_window : public t_window
{
public:
	t_spellbook_detail_window( t_screen_point point, t_abstract_creature const* creature, 
		                       t_window* parent, t_spellbook_window* spellbook,
							   t_cached_grail_data_source const & grail_data, t_window* banner, t_text_window* name );

	void set_spell( t_spellbook_window_data const& spell );
protected:
	void scroll_up( t_button * );
	void scroll_down( t_button * );
	void set_text_windows();

	t_artifact_icon*			m_artifact_icon;
	t_window*					m_banner;
	t_bitmap_group_ptr			m_book_bitmaps;
	t_button*                   m_cast_spell_button;
	t_abstract_creature const*	m_creature;
	int							m_current_scroll_position;
	t_bitmap_group_ptr			m_frame_bitmaps;
	t_cached_grail_data_source const & m_grail_data;
	t_bitmap_layer_ptr			m_large_spell_icon;
	t_blended_bitmap_window*	m_large_spell_icon_window;
	int							m_maximum_description_height;
	t_button_ptr				m_scroll_down_button;
	t_button_ptr				m_scroll_up_button;
	t_spellbook_window_data		m_spell;
	t_text_window*				m_spell_comment;
	t_bitmap_layer_window*		m_spell_frame;
	t_spell_icon_window*		m_spell_icon;
	t_bitmap_layer_window*		m_spell_icon_frame;
	t_text_window*				m_spell_name;
	t_text_window*				m_spell_text;
	int							m_spell_text_width;
	t_spellbook_window*         m_spellbook_window;
	t_string_vector				m_wrapped_spell_comment;
	t_string_vector				m_wrapped_spell_text;
};

namespace
{
	typedef void ( t_spellbook_window::* t_spellbook_button_function )( t_button* );

	typedef bool ( *t_spell_test )( t_abstract_creature const* creature );

	struct t_button_def
	{
		char const*		            name;
		t_spellbook_button_function	function;
		t_spell_test				test;
		char						hot_key;
	};

	class t_sort_spells
	{
	public:
		bool operator()( t_spellbook_window_data const& left, 
			             t_spellbook_window_data const& right ) const
		{
			if (get_spell_level( left.spell ) == get_spell_level( right.spell ))
				return string_insensitive_compare( get_spell_name( left.spell ),
				                                   get_spell_name( right.spell )) < 0;
			return get_spell_level( left.spell ) < get_spell_level( right.spell );
		}
	};

}; // unnamed namespace

t_bitmap_group_cache const			k_book_bitmaps( "dialog.spell_book" );
t_bitmap_group_cache const          k_spell_type_icons( "icons.spells.type" );

// -------------------------------------------------------------------
// get spell cost for spell
// -------------------------------------------------------------------
static int get_spell_cost( t_spellbook_window_data const& spell, 
						   t_abstract_creature const& creature )
{
	if (!spell.is_artifact)
		return creature.get_spell_cost( spell.spell );
	if (spell.potion_count > 0)
		return 0;
	return get_spell_cost( spell.spell );
}

// -------------------------------------------------------------------
// get spell cost for spell
// -------------------------------------------------------------------
static int get_spell_points( t_spellbook_window_data const& spell, 
						     t_abstract_creature const& creature )
{
	if (!spell.is_artifact)
		return creature.get_spell_points();
	return spell.spell_points;
}

// -------------------------------------------------------------------
// determine if creature has any spells in a catagory
// -------------------------------------------------------------------
static bool has_adventure( t_abstract_creature const* creature )
{
	t_spell spell;

	for (spell = t_spell(0); spell < k_spell_count; enum_incr(spell))
	{
		if (creature->knows_spell( spell ) && is_adventure( spell ))
			return true;
	}
	return false;
}

static bool has_blessings( t_abstract_creature const* creature )
{
	t_spell spell;

	for (spell = t_spell(0); spell < k_spell_count; enum_incr(spell))
	{
		if (creature->knows_spell( spell ) && is_blessing( spell ))
			return true;
	}
	return false;
}

static bool has_curses( t_abstract_creature const* creature )
{
	t_spell spell;

	for (spell = t_spell(0); spell < k_spell_count; enum_incr(spell))
	{
		if (creature->knows_spell( spell ) && is_curse( spell ))
			return true;
	}
	return false;
}

static bool has_damage( t_abstract_creature const* creature )
{
	t_spell spell;

	for (spell = t_spell(0); spell < k_spell_count; enum_incr(spell))
	{
		if (creature->knows_spell( spell ) && is_damage( spell ))
			return true;
	}
	return false;
}

static bool has_summoning( t_abstract_creature const* creature )
{
	t_spell spell;

	for (spell = t_spell(0); spell < k_spell_count; enum_incr(spell))
	{
		if (creature->knows_spell( spell ) && is_summoning( spell ))
			return true;
	}
	return false;
}

static bool has_combat( t_abstract_creature const* creature )
{
	t_spell spell;

	for (spell = t_spell(0); spell < k_spell_count; enum_incr(spell))
	{
		if (creature->knows_spell( spell ) && is_combat( spell ))
			return true;
	}
	return false;
}

static bool has_spell_items( t_abstract_creature const* creature )
{
	t_artifact_slot slot;

	// non-heroes cannot use artifacts
	if (creature->get_hero() == 0)
		return false; 
	for (slot = t_artifact_slot(0); slot < k_artifact_slot_hero_count; enum_incr( slot ))
	{
		if (creature->get_artifact( slot ).has_effect( k_artifact_effect_spell_charges ))
			return true;
	}
	int i;

	i = creature->get_backpack_count();
	while (i--)
	{
		if ( !creature->get_backpack_slot(i).is_potion() )
			continue;
		if (creature->get_backpack_slot(i).has_effect( k_artifact_effect_spell_charges ))
			return true;
	}
	return false;
}

static bool has_spells( t_abstract_creature const* creature, t_town_type type )
{
	t_spell spell;

	for (spell = t_spell(0); spell < k_spell_count; enum_incr(spell))
	{
		if (creature->knows_spell( spell ) && get_spell_alignment( spell ) == type )
			return true;
	}
	return false;
}

t_spellbook_window_data::t_spellbook_window_data( t_spell new_spell )
{
	spell = new_spell;
	is_artifact = false;
	spell_points = 0;
	max_spell_points = 0;
	slot = k_artifact_slot_none;
	potion_count = 0;
	backpack_slot = -1;
}

t_spellbook_window_data::t_spellbook_window_data( t_spell new_spell, int charge, int max_charge,
												  t_artifact_slot new_slot )
{
	spell = new_spell;
	is_artifact = true;
	spell_points = charge;
	max_spell_points = max_charge;
	slot = new_slot;
	potion_count = 0;
	backpack_slot = -1;
}

t_spellbook_window_data::t_spellbook_window_data( t_spell new_spell, int charge, int max_charge,
												  int new_slot )
{
	spell = new_spell;
	is_artifact = true;
	spell_points = charge;
	max_spell_points = max_charge;
	slot = slot;
	potion_count = 1;
	slot = k_artifact_slot_none;
	backpack_slot = new_slot;
}


// -------------------------------------------------------------------
// window to display spells available in mage guild
// -------------------------------------------------------------------
t_spellbook_window::t_spellbook_window( t_abstract_creature const* creature, t_window* parent, t_cached_grail_data_source const & grail_data, t_open_mode mode )
                  : t_window( k_completely_transparent, parent ),
				    m_grail_data( grail_data ),
				    m_selected_spell( k_spell_none ),
					m_tab_index( 0 )
{
	// load images and find dimensions of window.
	t_screen_rect rect;
	t_screen_rect parent_rect = get_parent()->get_client_rect();

	m_frame_bitmaps = k_frame_bitmaps.get();
	m_book_bitmaps = k_book_bitmaps.get();
	m_spell_type_icons = k_spell_type_icons.get();
	m_creature = creature;
	rect = m_book_bitmaps->get_rect();
	rect += (parent_rect.size() - rect.size()) / 2;
	init( rect );

	// create background
	t_bitmap_layer const* layer;
	t_screen_point        background_origin;
	t_window*             window;
	t_screen_point        zero(0,0);

	layer = m_book_bitmaps->find( "background" );
	background_origin = layer->get_rect().top_left();
//	background_origin = t_screen_point(44,44);
	window = new t_bitmap_layer_window( layer, zero, this );
	m_index_window = new t_window( layer->get_rect() - background_origin, 
		                           k_completely_transparent, window );

	// spell name backgrounds
	layer = m_book_bitmaps->find( "spell_name_background" );
	m_detail_banner[0] = new t_bitmap_layer_window( layer, zero - background_origin, window );
	m_detail_banner[0]->set_visible(false);

	layer = m_book_bitmaps->find( "spell_name_2_background" );
	m_detail_banner[1] = new t_bitmap_layer_window( layer, zero - background_origin, window );
	m_detail_banner[1]->set_visible(false);

	// create text windows
	rect = m_book_bitmaps->find( "spell_name" )->get_rect();
	m_detail_spell_name[0] = new t_text_window( get_font( rect.height() / 2 ), rect - background_origin, window, "", 
									  t_pixel_24(0,0,0));
	m_detail_spell_name[0]->set_center_horizontal();
	m_detail_spell_name[0]->set_center_vertical();
	m_detail_spell_name[0]->set_drop_shadow( true, t_pixel_24(200,200,200));

	rect = m_book_bitmaps->find( "spell_name_2" )->get_rect();
	m_detail_spell_name[1] = new t_text_window( get_font( rect.height() / 2 ), rect - background_origin, window, "", 
									  t_pixel_24(0,0,0));
	m_detail_spell_name[1]->set_center_horizontal();
	m_detail_spell_name[1]->set_center_vertical();
	m_detail_spell_name[1]->set_drop_shadow( true, t_pixel_24(200,200,200));

	// create detail windows
//	layer = m_book_bitmaps->find( "spell_name" );
	layer = m_book_bitmaps->find( "spell_details" );
	m_detail_window[0] = new t_spellbook_detail_window( layer->get_rect().top_left()
		                                                 - background_origin, m_creature,
														 window, this, m_grail_data, m_detail_banner[0], m_detail_spell_name[0] );
//	layer = m_book_bitmaps->find( "spell_name_2" );
	layer = m_book_bitmaps->find( "spell_details2" );
	m_detail_window[1] = new t_spellbook_detail_window( layer->get_rect().top_left()
		                                                 - background_origin, m_creature,
														 window, this, m_grail_data, m_detail_banner[1], m_detail_spell_name[1] );

	// create edges
	layer = m_book_bitmaps->find( "top" );
	window = new t_bitmap_layer_window( layer, zero, this );
	layer = m_book_bitmaps->find( "left" );
	window = new t_bitmap_layer_window( layer, zero, this );
	layer = m_book_bitmaps->find( "bottom" );
	window = new t_bitmap_layer_window( layer, zero, this );
	layer = m_book_bitmaps->find( "right" );
	window = new t_bitmap_layer_window( layer, zero, this );

	create_buttons( background_origin );
	create_labels( background_origin );
	create_spell_icons( background_origin );

	rect = m_book_bitmaps->find( "header_icon" )->get_rect();
	m_spell_type_window = new t_bitmap_layer_window( 0, rect.top_left() + t_screen_point(0,5), this );

	switch (mode)
	{
	  case k_open_combat:
		combat_spells_click( NULL );
		break;
	  case k_open_adventure:
		adventure_spells_click( NULL );
		break;
	  default:
		all_spells_click( NULL );
		break;
	}
}

// -------------------------------------------------------------------
// set new spell type
// -------------------------------------------------------------------
void t_spellbook_window::set_spell_type( char const* keyword )
{
	m_header_text = get_element_name( "spellbook", keyword );
	m_spell_type_window->set_bitmap( m_spell_type_icons->find( keyword ) );
	std::sort( m_spells.begin(), m_spells.end(), t_sort_spells());
	scroll_spells( 0 );
}

// -------------------------------------------------------------------
// window to display spells available in mage guild
// -------------------------------------------------------------------
void t_spellbook_window::add_spells( t_spell_tester const& valid_spell )
{
	t_spell spell;

	m_spells.clear();
	for (spell = t_spell(0); spell < k_spell_count; enum_incr(spell))
	{
		if (m_creature->knows_spell( spell ) && valid_spell( spell ))
			m_spells.push_back( spell );
	}
	add_artifacts( valid_spell );
}

// -------------------------------------------------------------------
// window to display spells available in mage guild
// -------------------------------------------------------------------
void t_spellbook_window::adventure_spells_click( t_button* button)
{
	if ( button && !button->is_pressed())
		return;
	add_spells( t_spell_func(is_adventure) );
	set_spell_type( "adventure" );
	m_tab_index = 5;
}

// -------------------------------------------------------------------
// window to display spells available in mage guild
// -------------------------------------------------------------------
void t_spellbook_window::all_spells_click( t_button* button )
{
	if ( button && button != 0 && !button->is_pressed())
		return;

	add_spells( t_spell_tester() );
	set_spell_type( "all" );
	m_tab_index = 4;
}

// -------------------------------------------------------------------
// window to display spells available in mage guild
// -------------------------------------------------------------------
void t_spellbook_window::combat_spells_click( t_button* button)
{
	if ( button && !button->is_pressed())
		return;

	add_spells( t_spell_func(is_combat) );
	set_spell_type( "combat" );
	m_tab_index = 6;
}

// -------------------------------------------------------------------
// window to display spells available in mage guild
// -------------------------------------------------------------------
void t_spellbook_window::curses_click( t_button* button )
{
	if (button && !button->is_pressed())
		return;
	add_spells( t_spell_func(is_curse) );
	set_spell_type( "curse" );
	m_tab_index = 1;
}

// -------------------------------------------------------------------
// window to display spells available in mage guild
// -------------------------------------------------------------------
void t_spellbook_window::blessings_click( t_button* button)
{
	if (button && !button->is_pressed())
		return;
	add_spells( t_spell_func(is_blessing) );
	set_spell_type( "blessings" );
	m_tab_index = 2;
}

// -------------------------------------------------------------------
// window to display spells available in mage guild
// -------------------------------------------------------------------
void t_spellbook_window::close_click( t_button* )
{
	m_return_value = false;
	close();
}

// -------------------------------------------------------------------
// window to display spells for a hero or creature
// -------------------------------------------------------------------
void t_spellbook_window::cast_spell_click( t_button*, t_spellbook_window_data const* spell )
{
	m_return_value = true;
	m_selected_spell = *spell;
	close();
}


// -------------------------------------------------------------------
// -------------------------------------------------------------------
void t_spellbook_window::cycle_spell_tabs( bool reverse )
{
//	::MessageBox("cycle_spell_tabs");
	int i;
	int ndx;

	if( reverse )
	{
		for( i = 1; i < 8; i++ )
		{
			ndx = ( m_tab_index - i ) % 8;
			if( ndx < 0 ) 
				ndx += 8;
			if(	m_tab_valid[ndx] )
			{
				m_tab_index = ndx;
				break;
			}
		}

		if( ndx == 8 )
			return;
	}
	else
	{
		for( i = 1; i < 8; i++ )
		{
			ndx = ( m_tab_index + i ) % 8;
			if(	m_tab_valid[ndx] )
			{
				m_tab_index = ndx;
				break;
			}
		}

		if( ndx == 8 )
			return;
	}

	switch( m_tab_index )
	{
	case 0:
		damage_spells_click(0);
		break;
	case 1:
		curses_click(0);
		break;
	case 2:
		blessings_click(0);
		break;
	case 3:
		summoning_click(0);
		break;
	case 4:
		all_spells_click(0);
		break;
	case 5:
		adventure_spells_click(0);
		break;
	case 6:
		combat_spells_click(0);
		break;
	case 7:
		items_click(0);
		break;
	default:
		break;
	}
}

// -------------------------------------------------------------------
// window to display spells available in mage guild
// -------------------------------------------------------------------
extern t_button_cache k_spellbook_index_button;

void t_spellbook_window::create_buttons( t_screen_point book_origin )
{
	t_toggle_button* toggle_button;
	t_screen_point   point;
	t_help_block const& help = get_help_block( "spellbook" );

	t_button_def button_definitions[] =
	{
		{ "damage",		&t_spellbook_window::damage_spells_click, has_damage, k_hot_key_damage.c_str()[0] },
		{ "curse",		&t_spellbook_window::curses_click, has_curses, k_hot_key_curses.c_str()[0] },
		{ "blessings",	&t_spellbook_window::blessings_click, has_blessings, k_hot_key_blessings.c_str()[0] },
		{ "summoning",	&t_spellbook_window::summoning_click, has_summoning, k_hot_key_summoning.c_str()[0] },
		{ "all",		&t_spellbook_window::all_spells_click, 0, k_hot_key_all_spells.c_str()[0] },
		{ "adventure",	&t_spellbook_window::adventure_spells_click, has_adventure, k_hot_key_adventure.c_str()[0] },
		{ "combat",		&t_spellbook_window::combat_spells_click, has_combat, k_hot_key_combat.c_str()[0] },
		{ "items",		&t_spellbook_window::items_click, has_spell_items, k_hot_key_artifacts.c_str()[0] },
	};

	int i;
	std::string layer_name;

	memset( &m_tab_valid, 0, sizeof( m_tab_valid ) );

	for (i = 0; i < ELEMENTS_OF( button_definitions ); i++)
	{
		t_button_def& definition = button_definitions[i];

		if (definition.test != 0 && !definition.test( m_creature ))
			continue;

		layer_name = definition.name;
		layer_name += "_released";
		point = m_book_bitmaps->find( layer_name )->get_rect().top_left();
		toggle_button = new t_toggle_button( point, this, true );
		set_button_layers( toggle_button, m_book_bitmaps, definition.name );
		toggle_button->set_click_handler( bound_handler( *this, definition.function ));
		layer_name = definition.name;
		layer_name += "_button";
		set_help( toggle_button, help, layer_name );
		m_tab_buttons.add( toggle_button );
		m_tab_valid[i] = true;
	}

	// close button
	t_button* button;
	
	button = create_button( m_book_bitmaps, "done", this, t_screen_point(0,0));
	button->set_click_handler( bound_handler( *this, &t_spellbook_window::close_click ));
	set_help( button, help, "close_button" );

	// school tabs
	for (i = 0; i < k_town_might; i++)
	{
		if (!has_spells( m_creature, t_town_type(i)))
			continue;

		layer_name = k_town_keyword[i];
		layer_name += "_released";
		point = m_book_bitmaps->find( layer_name )->get_rect().top_left();
		toggle_button = new t_toggle_button( point, this, true );
		set_button_layers( toggle_button, m_book_bitmaps, k_town_keyword[i] );
		toggle_button->set_click_handler( add_2nd_argument( bound_handler( *this,
										  &t_spellbook_window::school_click ),
										  t_town_type(i) ));
		layer_name = k_town_keyword[i];
		layer_name += "_button";
		set_help( toggle_button, help, layer_name );
		m_tab_buttons.add( toggle_button );
	}

	// last spell

	m_last_spell_button = create_button( m_book_bitmaps, "back", this, t_screen_point(0,0));
	m_last_spell_button->set_click_handler( bound_handler( *this, 
		                                    &t_spellbook_window::last_spell_click ));
	set_help( m_last_spell_button, help, "last_spell_button" );	

	m_next_spell_button = create_button( m_book_bitmaps, "forward", this, t_screen_point(0,0));
	m_next_spell_button->set_click_handler( bound_handler( *this, 
		                                    &t_spellbook_window::next_spell_click ));
	set_help( m_next_spell_button, help, "next_spell_button" );	

	// close detail
	point = m_book_bitmaps->find( "index_button" )->get_rect().top_left();
	m_index_button = new t_button( k_spellbook_index_button.get(), point, this );
	m_index_button->set_click_handler( bound_handler( *this, &t_spellbook_window::index_click ));
	set_help( m_index_button, help, "index_button" );
}

// -------------------------------------------------------------------
// window to display spells available in mage guild
// -------------------------------------------------------------------
void t_spellbook_window::create_labels( t_screen_point book_origin )
{
	t_screen_rect			rect;
	t_window*				window;
	t_text_window*          text_window;
	t_bitmap_layer const*	layer;
	t_help_block const&		help = get_help_block( "spellbook" );

	layer = m_book_bitmaps->find( "header_background" );
	rect = layer->get_rect();
	window = new t_bitmap_layer_window( layer, rect.top_left() - book_origin, m_index_window, false );

	rect = m_book_bitmaps->find( "header" )->get_rect() - book_origin;
	m_header = new t_text_window( get_font( rect.height() / 2 ), rect, m_index_window,
		                          "", t_pixel_24(0,0,0));
	m_header->set_center_horizontal();
	m_header->set_center_vertical();
	m_header->set_drop_shadow( true, t_pixel_24(200,200,200) );

	layer = m_book_bitmaps->find( "points_frame" );
	window = new t_bitmap_layer_window( layer, t_screen_point(0,0), this );
	set_help( window, help, "spell_points" );
	rect = m_book_bitmaps->find( "points" )->get_rect() - layer->get_rect().top_left();
	text_window = new t_text_window( get_font( rect.height() / 2 ), rect, window, 
		                             format_string( "%i", m_creature->get_spell_points() ),
									 t_pixel_24(255,255,255) );
	text_window->set_center_horizontal();
	text_window->set_center_vertical();
	text_window->set_drop_shadow( true, t_pixel_24(0,0,0));
}

// -------------------------------------------------------------------
// window to display spells available in mage guild
// -------------------------------------------------------------------
void t_spellbook_window::create_spell_icons( t_screen_point book_origin )
{
	t_screen_point        point;
	t_screen_point		  column_offset[k_spell_columns];
	t_screen_point        row_origin[k_spell_rows];
	int                   i;
	t_screen_point		  artifact_point;
	t_screen_rect         window_rect(0,0,0,0);
	t_screen_rect         name_rect;
	t_screen_rect         cost_rect;
	t_screen_rect         name_back_rect;
	t_screen_rect         cost_back_rect;
	t_screen_point        information_origin;
	std::string           layer_name;
	t_screen_rect		  frame_icon_rect;
	t_screen_rect         rect;
	t_screen_rect		  icon_rect;
	t_bitmap_layer const* name_back_layer;
	t_bitmap_layer const* cost_back_layer;
	t_bitmap_layer const* charges_layer;
	t_help_block const& help = get_help_block( "spellbook" );

	name_back_layer = m_book_bitmaps->find( "name_background" );
	charges_layer = m_book_bitmaps->find( "charges" );
	cost_back_layer = m_book_bitmaps->find( "cost_background" );
	frame_icon_rect = m_frame_bitmaps->find( "spell_icon" )->get_rect();
	icon_rect = m_book_bitmaps->find( "spell_1_row_1" )->get_rect();
	point = icon_rect.top_left();
	artifact_point = (icon_rect.size() - t_screen_point(44,44)) / 2;
	icon_rect -= point;
	column_offset[0] = t_screen_point(0,0);
	row_origin[0] = point - book_origin;
	name_rect = m_book_bitmaps->find( "name" )->get_rect();
	name_rect -= point;
	cost_rect = m_book_bitmaps->find( "cost" )->get_rect();
	cost_rect -= point;
	name_back_rect = name_back_layer->get_rect() - point;
	cost_back_rect = cost_back_layer->get_rect() - point;
	information_origin = point;
	column_offset[1] = m_book_bitmaps->find( "spell_2_row_1" )->get_rect().top_left() - point;

	for (i = 1; i < k_spell_rows; i++)
	{
		layer_name = format_string( "row_%i", i + 1 );
		row_origin[i] = m_book_bitmaps->find( layer_name )->get_rect().top_left() - book_origin;
	}

	int				row;
	int				column;
	
	i = 0;
	for (row = 0; row < k_spell_rows; row++)
	{
		for (column = 0; column < k_spell_columns; column++)
		{
			t_spell_slot& spell_info = m_spell_slot[i];

			point = row_origin[row] + column_offset[column];

			m_name_background[i] = new t_bitmap_layer_window( name_back_layer, point + name_back_rect.top_left(), m_index_window, false );
			m_name_background[i]->set_visible(false);

			m_cost_background[i] = new t_bitmap_layer_window( cost_back_layer, point + cost_back_rect.top_left(), m_index_window, false );
			m_cost_background[i]->set_visible(false);

			m_charges[i] = new t_bitmap_layer_window( charges_layer, point + cost_rect.top_left(), m_index_window, false );
			m_charges[i]->set_visible(false);

			// frame
			// graphic assignment is deferred until later
			m_spell_icon_frame[i] = new t_bitmap_layer_window( NULL, point, m_index_window, false );

			// icon and text
			spell_info.window = new t_window( window_rect + point, k_completely_transparent, 
				                              m_index_window );
			spell_info.button = new t_button( icon_rect.top_left(), spell_info.window );
			spell_info.spell_window = new t_spell_icon_window( k_spell_none, 
															  icon_rect + frame_icon_rect.top_left(), 
															  spell_info.button );
			spell_info.artifact_window = new t_artifact_icon( artifact_point + frame_icon_rect.top_left(),
				                                              spell_info.button );
			spell_info.button->set_released_image( spell_info.spell_window );
			spell_info.name = new t_text_window( get_font( name_rect.height() / 3 ),
				                                 name_rect, spell_info.window, "",
												 t_pixel_24(0,0,0));

			spell_info.name->set_center_horizontal();
			spell_info.name->set_center_vertical();
			spell_info.name->set_drop_shadow( true, t_pixel_24(200,200,200));
			spell_info.cost = new t_text_window( get_font( cost_rect.height() * 0.6 ),
				                                 cost_rect, spell_info.window, "",
												 t_pixel_24(0,0,0));
			spell_info.cost->set_center_horizontal();
			spell_info.cost->set_center_vertical();
			spell_info.cost->set_drop_shadow( true, t_pixel_24(200,200,200));
			spell_info.information_button = create_button( m_book_bitmaps, "information",
				                                           spell_info.window, 
														   information_origin );
			set_help( spell_info.information_button, help, "information" );

			++i;
		}
	}
}

// -------------------------------------------------------------------
// window to display spells available in mage guild
// -------------------------------------------------------------------
void t_spellbook_window::damage_spells_click( t_button* button)
{
	if ( button && !button->is_pressed())
		return;
	add_spells( t_spell_func(is_damage) );
	set_spell_type( "damage" );
	m_tab_index = 0;
}

// -------------------------------------------------------------------
// window to display spells available in mage guild
// -------------------------------------------------------------------
void t_spellbook_window::index_click( t_button* )
{
	scroll_spells( m_first_spell );
}

// -------------------------------------------------------------------
// window to display spells available in mage guild
// add an artifact to the spell book
// -------------------------------------------------------------------
void t_spellbook_window::add_artifact( t_artifact const& artifact, t_artifact_slot slot,
									   int backpack_slot, 
									   t_spell_tester const& valid_spell )
{
	if (!artifact.has_effect( k_artifact_effect_spell_charges ))
		return;

	t_artifact_effect_list const&			effects = artifact.get_effects();
	t_artifact_effect const*                effect;
	t_artifact_prop::t_spell_charges const* charges;
	int										effect_number;
	int									    i;
	bool									is_potion;
	t_spell									spell;
	int										spell_points;
	int										max_spell_points;

	is_potion = artifact.is_potion();
	for (effect_number = effects.size() - 1; effect_number >= 0; effect_number--)
	{
		effect = effects[effect_number];
		if (effect->get_effect() != k_artifact_effect_spell_charges)
			continue;
		charges = dynamic_cast<t_artifact_prop::t_spell_charges const*>(effect);
		if (charges == 0)
			continue;

		spell = charges->get_spell();
		spell_points = charges->get_spell_points();
		max_spell_points = charges->get_max_spell_points();
		if (!valid_spell( spell ))
			continue;

		for (i = 0; i < m_spells.size(); i++)
		{
			if (m_spells[i].spell == spell && m_spells[i].is_artifact)
			{
				if (is_potion)
				{
					if (m_spells[i].potion_count == 0)
						continue;
					m_spells[i].potion_count++;
					return;
				}

				assert( slot != k_artifact_slot_none );
				if (m_spells[i].spell_points < spell_points)
					m_spells[i] = t_spellbook_window_data( spell, spell_points, max_spell_points, slot );
				return;
			}
		}
		if (slot != k_artifact_slot_none)
			m_spells.push_back( t_spellbook_window_data( spell, spell_points, max_spell_points, slot ));
		else
			m_spells.push_back( t_spellbook_window_data( spell, spell_points, max_spell_points, backpack_slot ));
	}
}

// -------------------------------------------------------------------
// window to display spells available in mage guild
// -------------------------------------------------------------------
void t_spellbook_window::items_click( t_button* button)
{
	if (button && !button->is_pressed())
		return;
	m_spells.clear();

	add_artifacts( t_spell_tester() );
	set_spell_type( "items" );
	m_tab_index = 7;
}

// -------------------------------------------------------------------
// window to display spells available in mage guild
// -------------------------------------------------------------------
void t_spellbook_window::add_artifacts( t_spell_tester const& valid_spell )
{
	t_artifact_slot			 slot;

	// only heroes can use artifacts
	if (m_creature->get_hero() == 0)
		return;

	for (slot = t_artifact_slot(0); slot < k_artifact_slot_hero_count; enum_incr( slot ))
		add_artifact( m_creature->get_artifact( slot ), slot, -1, valid_spell );

	int i;

	i = m_creature->get_backpack_count();
	while (i--)
	{
		if ( m_creature->get_backpack_slot(i).is_potion() )
			add_artifact( m_creature->get_backpack_slot( i ), k_artifact_slot_none, i, valid_spell );
	}
}

// -------------------------------------------------------------------
// window to display spells available in mage guild
// -------------------------------------------------------------------
void t_spellbook_window::last_spell_click( t_button *)
{
	if (m_index_window->is_visible())
	{
		if (m_first_spell <= 0)
			return;
		m_first_spell -= k_total_spells;
		if (m_first_spell < 0)
			m_first_spell = 0;
		scroll_spells( m_first_spell );
		return;
	}
	if (m_current_spell <= 0)
		return;
	m_current_spell -= 2;
	if (m_current_spell < 0)
		m_current_spell = 0;
	spell_click( 0, m_current_spell );
}

// -------------------------------------------------------------------
// window to display spells available in mage guild
// -------------------------------------------------------------------
void t_spellbook_window::next_spell_click( t_button *)
{
	if (m_index_window->is_visible())
	{
		if (m_first_spell + k_total_spells >= m_spells.size())
			return;
		m_first_spell += k_total_spells;
		scroll_spells( m_first_spell );
		return;
	}
	if (m_current_spell + 2 >= m_spells.size())
		return;
	m_current_spell += 2;
	spell_click( 0, m_current_spell );
}

// -------------------------------------------------------------------
// window to display spells available in mage guild
// -------------------------------------------------------------------
void t_spellbook_window::school_click( t_button* button, t_town_type alignment )
{
	if (!button->is_pressed())
		return;

	t_school_tester valid_spell( alignment );

	add_spells( valid_spell );
	set_spell_type( k_town_keyword[alignment] );
}

// -------------------------------------------------------------------
// window to display spells available in mage guild
// -------------------------------------------------------------------
extern t_external_string k_text_cost;
static t_external_string const k_text_potion( "potion.spellbook" );
static t_external_string const k_text_potions( "potions.spellbook" );
static t_external_string const k_text_charge( "charge.spellbook" );

static std::string get_cost_string( t_spellbook_window_data const& spell_data, int cost )
{
	if (!spell_data.is_artifact)
		return k_text_cost.get() + format_string( " %i", cost );
	if (spell_data.potion_count == 0)
		return k_text_cost.get() + format_string( " %i ", cost );
	if (spell_data.potion_count == 1)
		return "1 " + k_text_potion.get();
	return format_string( "%i ", spell_data.potion_count ) + k_text_potions.get();
}

// -------------------------------------------------------------------
// window to display spells available in mage guild
// -------------------------------------------------------------------
void t_spellbook_window::scroll_spells( int first )
{
	int						i;
	std::string             cast_spell_help;
	std::string             cast_spell_right_click;
	t_bitmap_layer const *  frame;
	t_screen_rect			rect;
	
	m_spell_type_window->set_visible(true);
	cast_spell_help = get_element_name( "spellbook", "cast_spell" );
	cast_spell_right_click = get_element_help( "spellbook", "cast_spell" );
	m_detail_window[0]->set_visible( false );
	m_detail_window[1]->set_visible( false );
	m_detail_banner[0]->set_visible(false);
	m_detail_banner[1]->set_visible(false);
	m_detail_spell_name[0]->set_visible(false);
	m_detail_spell_name[1]->set_visible(false);
	m_index_button->set_visible( false );
	m_index_window->set_visible( true );
	m_first_spell = first;
	m_header->set_text( replace_keywords( m_header_text, "%page", 
		format_string( "%i", first / k_total_spells + 1 )));

	for (i = 0; i < k_total_spells; ++i)
	{
		m_spell_icon_frame[i]->set_visible(false);
		m_name_background[i]->set_visible(false);
		m_cost_background[i]->set_visible(false);
		m_charges[i]->set_visible(false);
	}

	for (i = 0; i < k_total_spells && i + m_first_spell < m_spells.size(); i++)
	{
		t_spell_slot&					spell_info = m_spell_slot[i];
		t_spell							spell;
		int								cost;
		int								spell_points;
		std::string						cost_string;
		std::string						text;
		t_button_handler				handler;
		t_spellbook_window_data const&  spell_data = m_spells[i + m_first_spell];

		spell = spell_data.spell;

		// frame
		if( spell_info.artifact_window || spell_info.spell_window )
		{
			t_town_type alignment;
			alignment = get_spell_alignment( spell );
			if( spell_data.is_artifact )
				frame = m_frame_bitmaps->find( "generic" );
			else
				frame = m_frame_bitmaps->find( k_town_keyword[alignment] );
			rect = frame->get_rect();
			m_spell_icon_frame[i]->set_bitmap(frame);
			m_spell_icon_frame[i]->set_visible(true);
			m_name_background[i]->set_visible(true);
			m_cost_background[i]->set_visible(true);

		}

		t_artifact const* artifact;
		if (spell_data.is_artifact)
		{
			if (spell_data.slot != k_artifact_slot_none)
				artifact = &m_creature->get_artifact( spell_data.slot );
			else
				artifact = &m_creature->get_backpack_slot( spell_data.backpack_slot );
			spell_info.artifact_window->set_artifact( *artifact );
			spell_info.button->set_released_image( spell_info.artifact_window );

			// if it's a wand, show charges remaining graphic
			if( artifact->is_wand() )
			{
				float charge_percentage;
				t_screen_rect rect = m_book_bitmaps->find( "cost" )->get_rect();
				t_screen_rect charges_rect = m_charges[i]->to_parent( m_charges[i]->get_screen_rect() );

				charge_percentage = (float)spell_data.spell_points / (float)spell_data.max_spell_points;
				rect.left = charges_rect.left;
				rect.top = charges_rect.top;
				rect.right = rect.left + charges_rect.width();
				rect.bottom = rect.top + charges_rect.height();
				rect.right = rect.left + ( rect.width() * charge_percentage );

				m_charges[i]->move(rect);
				m_charges[i]->set_visible(true);
			}
		}
		else
		{
			artifact = 0;
			spell_info.spell_window->set_spell( spell );
			spell_info.button->set_released_image( spell_info.spell_window );
		}
		m_spell_icon_frame[i]->set_bitmap(frame);
		spell_info.spell_window->set_visible( !spell_data.is_artifact );
		spell_info.artifact_window->set_visible( spell_data.is_artifact );

//		text += "\n";
		text += replace_keywords( get_spellbook_text( spell ), m_creature, spell, m_grail_data, artifact );
		spell_info.name->set_text( text );
		cost = get_spell_cost( spell_data, *m_creature );
		spell_points = get_spell_points( spell_data, *m_creature );
		cost_string = get_cost_string( spell_data, cost );
		spell_info.cost->set_text( cost_string );
		if (cost > spell_points)
		{
			spell_info.cost->set_color( t_pixel_24(170,0,0));
			spell_info.button->enable( false );
		}
		else
		{
			spell_info.cost->set_color( t_pixel_24(0,0,0));
			spell_info.button->enable( true );
		}
		spell_info.button->set_help_balloon_text( 
			replace_keywords( cast_spell_help, "%spell_name", get_spell_name( spell )));
		spell_info.button->set_right_click_text(
			replace_keywords( cast_spell_right_click, "%spell_name", get_spell_name( spell ) ) );
		handler = add_2nd_argument( bound_handler( *this, &t_spellbook_window::cast_spell_click ),
			                        &spell_data );
		spell_info.button->set_click_handler( handler );

		spell_info.information_button->set_click_handler( add_2nd_argument( bound_handler( *this, 
			                                              &t_spellbook_window::spell_click ), 
											              i + m_first_spell ) );

		spell_info.window->set_size_from_children();
		spell_info.window->set_visible( true );
	}
	while (i < k_total_spells)
	{
		t_spell_slot&			spell_info = m_spell_slot[i];

		spell_info.window->set_visible( false );
		i++;
	}
	// set next / last button visibility
	m_last_spell_button->set_visible( m_first_spell > 0 );
	m_next_spell_button->set_visible( m_first_spell + k_total_spells < m_spells.size() );
}


// -------------------------------------------------------------------
// window to display spells available in mage guild
// -------------------------------------------------------------------
void t_spellbook_window::spell_click( t_button*, int index )
{
	m_spell_type_window->set_visible(false);
	m_index_window->set_visible( false );
	m_index_button->set_visible( true );
	m_current_spell = (index / 2) * 2;

	m_detail_window[0]->set_spell( m_spells[m_current_spell] );
	m_detail_window[0]->set_visible(true);
	m_detail_banner[0]->set_visible(true);
	m_detail_spell_name[0]->set_visible(true);
	if (m_current_spell + 1 >= m_spells.size())
	{
		m_detail_window[1]->set_visible(false);
		m_detail_banner[1]->set_visible(false);
		m_detail_spell_name[1]->set_visible(false);
	}
	else
	{
		m_detail_window[1]->set_spell( m_spells[m_current_spell + 1] );
		m_detail_window[1]->set_visible( true );
		m_detail_banner[1]->set_visible(true);
		m_detail_spell_name[1]->set_visible(true);
	}

	m_last_spell_button->set_visible( m_current_spell > 0 );
	m_next_spell_button->set_visible( m_current_spell + 2 < m_spells.size() );
}

// -------------------------------------------------------------------
// window to display spells available in mage guild
// -------------------------------------------------------------------
void t_spellbook_window::summoning_click( t_button* button)
{
	if (button && !button->is_pressed())
		return;
	add_spells( t_spell_func(is_summoning) );
	set_spell_type( "summoning" );
	m_tab_index = 3;
}

// ------------------------------------------------------------------
// window to show detail of a particular spell
// ------------------------------------------------------------------
extern t_font_cache k_script_font;

t_spellbook_detail_window::t_spellbook_detail_window( t_screen_point point, 
													  t_abstract_creature const* creature, 
													  t_window* parent, 
													  t_spellbook_window* spellbook,
													  t_cached_grail_data_source const & grail_data,
													  t_window* banner, t_text_window* name )
                         : t_window( t_screen_rect(point.x,point.y, point.x, point.y),
						             k_completely_transparent, parent ),
						   m_grail_data( grail_data ),
						   m_spell( k_spell_none )
{
	t_screen_rect rect;
	t_screen_rect frame_icon_rect;

	m_spellbook_window = spellbook;
	m_book_bitmaps = k_book_bitmaps.get();
	m_creature = creature;
	m_frame_bitmaps = k_frame_bitmaps.get();
	m_banner = banner;
	m_spell_name = name;

	frame_icon_rect = m_frame_bitmaps->find( "spell_icon" )->get_rect();

	// get the point the layout is based on
	rect = m_book_bitmaps->find( "spell_details" )->get_rect();
	point = rect.top_left();

	// create text windows
//	rect = m_book_bitmaps->find( "spell_name" )->get_rect() - point;
//	m_spell_name = new t_text_window( get_font( rect.height() ), rect, this, "", 
//		                              t_pixel_24(0,0,0));
//	m_spell_name->set_center_horizontal();
//	m_spell_name->set_drop_shadow( true, t_pixel_24(200,200,200));

	rect = m_book_bitmaps->find( "description_text" )->get_rect() - point;
	m_spell_text = new t_text_window( get_font( 20 ), rect, this, "", t_pixel_24(0,0,0), true );
	m_spell_text->set_drop_shadow( true, t_pixel_24(200,200,200));
	m_maximum_description_height = rect.height();
	m_spell_text_width = rect.width();

	m_spell_comment = new t_text_window( k_script_font.get(), rect, this, "", t_pixel_24(0,0,0), true );
	m_spell_comment->set_drop_shadow( true, t_pixel_24(200,200,200));

	// Create scroll up / scroll down buttons
	// Log : gameart

/*	rect = m_book_bitmaps->find( "spell_details_scroll_up" )->get_rect() - point;
    m_scroll_up_button = new t_button( rect.top_left(), this );
	set_button_layers( m_scroll_up_button, k_arrow_button_bitmaps.get(), "up" );
	m_scroll_up_button->set_click_handler( bound_handler( *this, &t_spellbook_detail_window::scroll_up ) );

	rect = m_book_bitmaps->find( "spell_details_scroll_down" )->get_rect() - point;
    m_scroll_down_button = new t_button( rect.top_left(), this );
	set_button_layers( m_scroll_down_button, k_arrow_button_bitmaps.get(), "down" );
	m_scroll_down_button->set_click_handler( bound_handler( *this, &t_spellbook_detail_window::scroll_down ) );
*/
	// create cast spell button
	rect = m_book_bitmaps->find( "cast_spell_detail" )->get_rect() - point;
	m_cast_spell_button = new t_button( k_cast_spell_button.get(), rect.top_left(), this );

	// frame
	// graphic assignment is deferred until later
	rect = m_book_bitmaps->find( "spell_details" )->get_rect() - point;
	m_spell_icon_frame = new t_bitmap_layer_window( NULL, rect.top_left(), this, false );
	rect += frame_icon_rect.top_left();

	// create icons
	m_spell_icon = new t_spell_icon_window( k_spell_none, rect, this );
	m_artifact_icon = new t_artifact_icon( rect.top_left() 
		                                   + (rect.size() - t_screen_point(44,44)) / 2,
										   this );
	rect = m_book_bitmaps->find( "icon_silhouette" )->get_rect() - point;
	m_large_spell_icon_window = new t_blended_bitmap_window( 0, rect.top_left(), this, true, 4 );
	m_spell = k_spell_none;
}

static int const k_comment_separator_height = 20; // pixels between main text and flavor text

// ------------------------------------------------------------------
// window to show detail of a particular spell
// ------------------------------------------------------------------
void t_spellbook_detail_window::set_spell( t_spellbook_window_data const& spell )
{
	if (spell.spell == m_spell.spell)
		return;
	
	std::string				text = get_spell_help( spell.spell );
	std::string				flavor_text = get_spell_flavor_text( spell.spell );
	int						spell_cost;
	int						spell_points;
	t_bitmap_layer const *  frame;

	// set spell icon
	std::string spell_name = get_spell_name( spell.spell );

	m_artifact_icon->set_visible( spell.is_artifact );
	m_spell_icon->set_visible( !spell.is_artifact );
	if (spell.is_artifact)
	{
		t_artifact const* artifact;

		if (spell.slot != k_artifact_slot_none)
			artifact = &m_creature->get_artifact( spell.slot );
		else
			artifact = &m_creature->get_backpack_slot( spell.backpack_slot );
		m_artifact_icon->set_artifact( *artifact );
		spell_name = artifact->get_name();
		text = artifact->get_help_text();
//		flavor_text = "";
	}
	else
	{
		spell_name = get_spell_name( spell.spell );
		m_spell_icon->set_spell( spell.spell );
	}

	m_banner->set_visible(true);
	m_spell_name->set_visible(true);
	m_current_scroll_position = 0;

	m_spell = spell;
	spell_cost = get_spell_cost( spell, *m_creature );
	spell_points = get_spell_points( spell, *m_creature );
	text += "\n\n";
	text += get_cost_string( spell, spell_cost );

	std::string effect_text = get_spellbook_text( spell.spell );
//
	effect_text = replace_keywords( effect_text, "%spell_name", "" );
	if (!effect_text.empty())
		text += "\n" + replace_keywords( effect_text, m_creature, 
										 spell.spell, m_grail_data );

	t_cached_font const & text_font = m_spell_text->get_font();
	text_font->wrap_text( m_wrapped_spell_text, text.c_str(), m_spell_text_width );

	t_cached_font const & flavor_font = m_spell_comment->get_font();
	flavor_font->wrap_text( m_wrapped_spell_comment, flavor_text.c_str(), m_spell_text_width );
//
	int           height =   ( text_font->get_height() * m_wrapped_spell_text.size() )
						   + k_comment_separator_height // Arbitrary separator
						   + ( flavor_font->get_height() * m_wrapped_spell_comment.size() );

	if ( height > m_maximum_description_height )
	{
//		m_scroll_down_button->set_visible( true );
//		m_scroll_up_button->set_visible( true );
	}
	else
	{
//		m_scroll_down_button->set_visible( false );
//		m_scroll_up_button->set_visible( false );
	}

	set_text_windows();

	// frame
	t_town_type alignment;
	alignment = get_spell_alignment( spell.spell );
	if( spell.is_artifact )
		frame = m_frame_bitmaps->find( "generic" );
	else
		frame = m_frame_bitmaps->find( k_town_keyword[alignment] );
	t_screen_rect rect = frame->get_rect();
	m_spell_icon_frame->set_bitmap(frame);
	m_spell_icon_frame->set_visible(true);

	m_spell_name->set_text( spell_name );

	// set large spell window
	m_large_spell_icon = get_spell_icon( spell.spell, 180 );
	m_large_spell_icon_window->set_bitmap( m_large_spell_icon );

	//set help on cast spell button
	t_button_handler handler;

	handler = add_2nd_argument<t_spellbook_window_data const*>(
		      bound_handler( *m_spellbook_window, &t_spellbook_window::cast_spell_click ),
			  &m_spell );
	m_cast_spell_button->set_click_handler( handler );
	text = get_element_name( "spellbook", "cast_spell" );
	m_cast_spell_button->set_help_balloon_text( replace_keywords( text, "%spell_name", 
		                                        spell_name ));
	text = get_element_help( "spellbook", "cast" );

	t_help_block const& help = get_help_block( "spellbook" );
	t_item_help const& item = help["cast"];
	m_cast_spell_button->set_right_click_text( item.help_text );
	m_cast_spell_button->enable( spell_points >= spell_cost );

	set_size_from_children();
}

void t_spellbook_detail_window::scroll_down( t_button * )
{
	// Note: past-end-of-scroll-area handled by set_text_windows()
	m_current_scroll_position++;
	set_text_windows();
}

void t_spellbook_detail_window::scroll_up( t_button * )
{
	if ( m_current_scroll_position > 0 )
	{
		m_current_scroll_position--;
		set_text_windows();
	}
}

// ------------------------------------------------------------------
// Do everything needed to display the text in the text windows
// ------------------------------------------------------------------
void t_spellbook_detail_window::set_text_windows()
{
	// Just in case
	if ( m_current_scroll_position < 0 )
		m_current_scroll_position = 0;

	int           height;

	t_screen_rect text_rect = m_spell_text->get_screen_rect();

	t_cached_font const & text_font = m_spell_text->get_font();

	if ( m_current_scroll_position >= m_wrapped_spell_text.size() )
	{
		m_spell_text->set_visible( false );

		height = k_comment_separator_height - ( text_font->get_height() * ( m_current_scroll_position - m_wrapped_spell_text.size() ) );

		if ( height < 0 )
			height = 0;
	}
	else
	{
		m_spell_text->set_visible( true );

		// Display normal text
		t_string_vector displayed_text;

		t_string_vector::iterator text_start;
		text_start = m_wrapped_spell_text.begin() + m_current_scroll_position;
		displayed_text.assign( text_start, m_wrapped_spell_text.end() );
	
		m_spell_text->set_wrapped_text( displayed_text, m_spell_text_width );


		t_screen_rect rect = text_rect;
		height = m_spell_text->get_text_height();

		if (height > m_maximum_description_height)
			height = m_maximum_description_height;
		rect.bottom = rect.top + height;
		m_spell_text->move_screen_rect( rect );

		height += k_comment_separator_height;
	}

	t_cached_font const & flavor_font = m_spell_comment->get_font();

	if ( height + flavor_font->get_height() > m_maximum_description_height )
	{
		m_spell_comment->set_visible( false );
		m_scroll_down_button->enable( true );
	}
	else
	{
		m_spell_comment->set_visible( true );

		t_screen_rect	flavor_rect = text_rect;

		flavor_rect.top = text_rect.top + height;
		flavor_rect.bottom = text_rect.top + m_maximum_description_height;

		m_spell_comment->move_screen_rect( flavor_rect );

		int flavor_lines = flavor_rect.height() / flavor_font->get_height();

		if ( flavor_lines > m_wrapped_spell_comment.size() )
			flavor_lines = m_wrapped_spell_comment.size();

		t_string_vector displayed_flavor_text;

		t_string_vector::iterator flavor_text_end;

		flavor_text_end = m_wrapped_spell_comment.begin() + flavor_lines;
		displayed_flavor_text.assign( m_wrapped_spell_comment.begin(), flavor_text_end );

		m_spell_comment->set_wrapped_text( displayed_flavor_text, m_spell_text_width );

//		m_scroll_down_button->enable( flavor_lines < m_wrapped_spell_comment.size() );
	}

//	m_scroll_up_button->enable( m_current_scroll_position > 0 );
}

